﻿using CQIE.TES.IDAL;
using CQIE.TES.Model;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CQIE.TES.WEB.USER.Models;
using Newtonsoft.Json.Linq;

namespace CQIE.TES.DAL
{
    public class RecordDAL: DALBase<Record>,IRecordDAL
    {
        private IEvaluaHeightDAL evaluaHeightDal { get; set; }
        public RecordDAL(IEvaluaHeightDAL evaluaHeightDal)
        {
            this.evaluaHeightDal = evaluaHeightDal;
        }

        #region 评教方面

        public bool SaveStudentRecord(int evaId, int teacherid, int courseId, int studentId, List<int> answerList, List<int> question)
        {
            Record model = new Record();
            model.EvaId = evaId;
            model.CourseID = courseId;  
            model.AimTeacher = teacherid;
            model.Type = (int)ENUM_EVA_TYPE.Student;
            model.Time = DateTime.Now;
            
            List<object> list = new List<object>();
            for (int i = 0; i < answerList.Count; i++)
            {
                list.Add(new
                {
                    qid=question[i],
                    aid =answerList[i]
                });
            }
            JArray jsonarr = JArray.FromObject(list);
            model.TheLog = jsonarr.ToString();
            using (MyDB db = new MyDB())
            {
                model.S_WNumber = db.Student.Find(studentId)?.StudentNumber;
                model.Score = getScore(answerList, question);
                model.StudyYearID = db.EvaluaHeight.Find(evaId).StudyYearId;
                db.Record.Add(model);

                if (db.SaveChanges()>0)
                {
                    return true;
                }

                return false;
            }
        }

        public List<Record> IsRecorded(int evaid, int teacherid, int courseId, string studentCode)
        {
            using (MyDB db = new MyDB())
            {

                var list = db.Record.Where(r =>
                    r.EvaId == evaid && r.AimTeacher == teacherid && r.CourseID == courseId &&
                    r.S_WNumber == studentCode && r.Type == (int)ENUM_EVA_TYPE.Student).ToList();

                return list;
            }
        }

        public List<Record> IsRecorded(int evaId, Teacher teacher, int aimTeacherId,ENUM_EVA_TYPE mode)
        {
            using (MyDB db = new MyDB())
            {
                var list = db.Record
                    .Where(r => r.EvaId == evaId && r.AimTeacher == aimTeacherId && r.S_WNumber == teacher.WorkNumber && r.Type==(int)mode)
                    .ToList();

                return list;
            }
        }

        public List<Record> IsRecorded(int evaId, Teacher teacher)
        {
            using (MyDB db = new MyDB())
            {
                var list = db.Record.Where(r => r.EvaId == evaId && r.S_WNumber == teacher.WorkNumber && r.AimTeacher==teacher.ID && r.Type==(int)ENUM_EVA_TYPE.Self).ToList();
                return list;
            }
        }

        public bool SaveDepartmentRecord(int evaId, string teacherId, int aimTeacherId, List<int> answerList,
            List<int> questionList, ENUM_EVA_TYPE mode)
        {
            using (MyDB db = new MyDB())    
            {
                EvaluaHeight eva = db.EvaluaHeight.Find(evaId);
                Record model = new Record();
                model.AimTeacher = aimTeacherId;
                model.S_WNumber = teacherId;
                model.StudyYearID = eva.StudyYear.ID;
                model.Eva = eva;
                model.Type = (int)mode;
                model.Time = DateTime.Now;
                // TheLog  Score
                model.Score = getScore(answerList, questionList);

                List<object> list = new List<object>();
                for (int i = 0; i < answerList.Count; i++)
                {
                    list.Add(new
                    {
                        qid = questionList[i],
                        aid = answerList[i]
                    });
                }
                model.TheLog = JArray.FromObject(list).ToString();
                db.Record.Add(model);
                if (db.SaveChanges()>0)
                {
                    return true;
                }

                return false;
            }
        }

        public bool SaveTeacherOtherRecord(int evaId, string teacherId, int aimTeacherId, List<int> answerList, List<int> questionList)
        {
            using (MyDB db = new MyDB())
            {
                EvaluaHeight eva = db.EvaluaHeight.Find(evaId);
                Record model = new Record();
                model.AimTeacher = aimTeacherId;
                model.S_WNumber = teacherId;
                model.StudyYearID = eva.StudyYear.ID;
                model.Eva = eva;
                model.Type = (int)ENUM_EVA_TYPE.Teacher;
                model.Time = DateTime.Now;
                // TheLog  Score
                model.Score = getScore(answerList, questionList);

                List<object> list = new List<object>();
                for (int i = 0; i < answerList.Count; i++)
                {
                    list.Add(new
                    {
                        qid = questionList[i],
                        aid = answerList[i]
                    });
                }
                model.TheLog = JArray.FromObject(list).ToString();
                db.Record.Add(model);
                if (db.SaveChanges() > 0)
                {
                    return true;
                }

                return false;
            }
        }


        public bool SaveTeacherSelfRecord(int evaId, int teacherId, string teacherNum, List<int> answerList,
            List<int> questionList)
        {
            using (MyDB db = new MyDB())
            {
                EvaluaHeight eva = db.EvaluaHeight.Find(evaId);
                Record model = new Record();
                model.AimTeacher = teacherId;
                model.S_WNumber = teacherNum;
                model.StudyYearID = eva.StudyYear.ID;
                model.Eva = eva;
                model.Type = (int)ENUM_EVA_TYPE.Self;
                model.Time = DateTime.Now;
                // TheLog  Score
                model.Score = getScore(answerList, questionList);

                List<object> list = new List<object>();
                for (int i = 0; i < answerList.Count; i++)
                {
                    list.Add(new
                    {
                        qid = questionList[i],
                        aid = answerList[i]
                    });
                }
                model.TheLog = JArray.FromObject(list).ToString();
                db.Record.Add(model);
                if (db.SaveChanges() > 0)
                {
                    return true;
                }

                return false;
            }
        }

        #endregion

        #region 查看结果

        public List<Record> GetTeacherRecord(int teacherId, int? courseId = null, int? studyYearId = null)
        {
            using (MyDB db = new MyDB())
            {
                var record = db.Record.Include(r => r.Eva).Where(r => r.AimTeacher == teacherId).ToList();
                if (courseId.HasValue && !studyYearId.HasValue)
                {
                    record = record.Where(r => r.CourseID == courseId).ToList();
                }
                if (!courseId.HasValue && studyYearId.HasValue)
                {
                    record = record.Where(r => r.Eva.StudyYearId==studyYearId).ToList();
                }

                if (courseId.HasValue && studyYearId.HasValue)
                {
                    record = record.Where(r => r.Eva.StudyYearId == studyYearId && r.CourseID == courseId).ToList();
                }
                
                return record;
            }
        }

        public object GetEvaTrend()
        {
            using (MyDB db = new MyDB())
            {
                var studyYearList = db.StudyYear.ToList();
                var lineData = new
                {
                    l1 = new List<int>(),
                    l2 = new List<int>(),
                    l3 = new List<int>(),
                    l4 = new List<int>(),
                    studyYear = studyYearList.Select(r => r.Name)
                };
                // 提前读取
                var teacherList = db.Teacher.Include(r => r.Record).ToList();
                var evaList = db.EvaluaHeight.ToList();

                List<object> tempData = new List<object>();
                List<Teacher> data = new List<Teacher>();
                studyYearList.ForEach(y =>
                {
                    // 准备折线图数据
                    data = GetLeaveCount(y.ID,teacherList,evaList);
                    lineData.l1.Add(data.Count(r => r.TotalYearScore >= 50));
                    lineData.l2.Add(data.Count(r => r.TotalYearScore >= 40 && r.TotalYearScore < 50));
                    lineData.l3.Add(data.Count(r => r.TotalYearScore < 40 && r.TotalYearScore >= 25));
                    lineData.l4.Add(data.Count(r => r.TotalYearScore < 25));

                    // 准备饼图数据
                    data.ForEach(t => tempData.Add(new { tid = t.ID, score = t.TotalYearScore, studyYear = y }));
                });

                data.ForEach(t =>
                {
                    double total = 0;
                    var temp = tempData.Where(e => t.ID == (int)e.GetType().GetProperty("tid").GetValue(e)).ToList();
                    temp.ForEach(j =>
                    {
                        total += (double)j.GetType().GetProperty("score").GetValue(j);

                    });
                    t.TotalAllScore = Math.Round(total / temp.Count, 2);
                });
                List<object> pieData = new List<object>();
                pieData.Add(new { name = "优秀", value = data.Count(r => r.TotalAllScore >= 50) });
                pieData.Add(new { name = "良好", value = data.Count(r => r.TotalAllScore >= 40 && r.TotalAllScore < 50) });
                pieData.Add(new { name = "一般", value = data.Count(r => r.TotalAllScore >= 25 && r.TotalAllScore < 40) });
                pieData.Add(new { name = "较差", value = data.Count(r => r.TotalAllScore < 25) });
                return new
                {
                    lineData,
                    pieData
                };
            }

        }

        public object GetTableData(int limit, int page, string searchString, out int count)
        {
            using (MyDB db = new MyDB())
            {
                var data = db.Teacher.ToList();
                List<Teacher> teachers = new List<Teacher>();
                if (searchString==null)
                {
                    teachers = data.OrderBy(r => r.ID).Skip(page * limit).Take(limit).ToList();
                }
                else
                {
                    teachers = data.Where(r=>r.Name.Contains(searchString)).OrderBy(r => r.ID).Skip(page * limit).Take(limit).ToList();
                }
                
                count = data.Count;

                return teachers.Select(r => new
                    {name = r.Name, id = r.ID, score = Math.Round(GetTeacherScoreTrend(r.ID).Average(), 2)});
            }
        }

        public object GetCourseGrade(int tid)
        {
            using (MyDB db = new MyDB())
            {
                var teacherCourse = db.Teacher_Course.Where(r => r.TeacherID == tid).ToList();
                List<string> course = new List<string>();
                List<double> value = new List<double>();

                teacherCourse.ForEach(r =>
                {
                    var records = db.Record.Where(e => e.CourseID == r.CourseID && e.AimTeacher == tid).ToList();

                    if (records.Count > 0)
                    {
                        course.Add(r.Course.Name);
                        value.Add(Math.Round((double)records.Average(a => a.Score), 2));
                    }
                });

                return new
                {
                    title = course,
                    value
                };
            }
        }

        public List<double> GetTeacherScoreTrend(int teacherId)
        {
            using (MyDB db = new MyDB())
            {
                var studyYear = db.StudyYear.ToList();
                List<double> scoreTrend = new List<double>();
                var EvaList = db.EvaluaHeight.ToList();
                studyYear.ForEach(year =>
                {
                    double total = 0; // 存放年度总成绩 
                    var temp = EvaList.Where(e => e.StudyYearId == year.ID).ToList();
                    var records = GetTeacherRecord(teacherId, studyYearId: year.ID);
                    temp.ForEach(e =>
                    {
                        total += GetTeacherAvgGrade(records.Where(r => r.EvaId == e.ID).ToList(), e);
                    });
                    scoreTrend.Add(Math.Round(total / temp.Count, 2));
                });
                return scoreTrend;
            }
        }

        private List<Teacher> GetLeaveCount(int studyYearId,List<Teacher> teacherList,List<EvaluaHeight> evaList)
        {
            using (MyDB db = new MyDB())
            {
                // 学年-评教→教师→记录→计算分数
                evaList = evaList.Where(r => r.StudyYearId == studyYearId).ToList();

                teacherList.ForEach(t =>
                {
                    double totalScore = 0;
                    evaList.ForEach(e =>
                    {
                        // 找到当前学年 当前评教的记录
                        var yearData = t.Record.Where(r => r.EvaId == e.ID).ToList();
                        totalScore += GetTeacherAvgGrade(yearData, e);
                    });
                    t.TotalYearScore = Math.Round(totalScore / evaList.Count, 2);
                });

                return teacherList;

            }
        }

        public object GetClassScore(int teacherId)
        {
            using (MyDB db = new MyDB())
            {
                var studentRecord = db.Record.Where(r => r.Type == (int) ENUM_EVA_TYPE.Student);
                Func<Class_, List<int>> getClassInfo = myClass =>
                {
                    int l1 = 0;
                    int l2 = 0;
                    int l3 = 0;
                    int l4 = 0;
                    foreach (Student student in myClass.Student)
                    {
                        var score = studentRecord.Where(r => r.S_WNumber == student.StudentNumber).Average(r => r.Score);

                        if (score >= 50)
                        {
                            l1++;
                        }

                        if (score >= 40 && score < 50)
                        {
                            l2++;
                        }

                        if (score >= 25 && score < 40)
                        {
                            l3++;
                        }

                        if (score < 25)
                        {
                            l4++;
                        }
                    }
                    return new List<int>() { l1, l2, l3, l4 };
                };

                // 找到教师所有任课班级
                var data = db.Class_Course.Where(r => r.CourseInfo.TeacherID == teacherId).Select(r=>r.Class_).ToList();
                var res = new
                {
                    A = new List<int>(),
                    B = new List<int>(),
                    C = new List<int>(),
                    D = new List<int>(),
                    ClassInfo = data.Select(c => c.ClassCode).ToList()
                };

                List<Student> students = new List<Student>();
                

                data.ForEach(c =>
                {
                    students.AddRange(c.Student);
//                    var temp = getClassInfo(c);
//                    res.A.Add(temp[0]);
//                    res.B.Add(temp[1]);
//                    res.C.Add(temp[2]);
//                    res.D.Add(temp[3]);
                });

                return res;
            }
        }

        public object CalcEvaTypeNum(int teacherId,ENUM_EVA_TYPE type)
        {
            using (MyDB db = new MyDB())
            {
                List<object> res = new List<object>();
                var data = db.Teacher.Find(teacherId)?.Record.Where(r => r.Type == (int) type);

                res.Add(new {name = "优秀", value = data.Count(r => r.Score >= 50)});
                res.Add(new {name = "良好", value = data.Count(r => r.Score >= 40 && r.Score < 50)});
                res.Add(new {name = "一般", value = data.Count(r => r.Score >= 25 && r.Score < 40)});
                res.Add(new {name = "较差", value = data.Count(r => r.Score < 25)});
                return res;
            }
        }

        /// <summary>
        /// 计算一系列记录的平均分
        /// </summary>
        /// <param name="yearRecords">记录集合</param>
        /// <param name="eva">评教</param>
        /// <returns></returns>
        private double GetTeacherAvgGrade(List<Record> yearRecords,EvaluaHeight eva)
        {
            // 计算记录中的平均成绩
            Func<ENUM_EVA_TYPE, double> getAvgScore = evaType =>
            {
                double total = 0;
                var temp = yearRecords.Where(r => r.Type == (int) evaType);
                foreach (Record r in temp)
                {
                    if (r.Score != null) total += (double) r.Score;
                }

                double tres = Math.Round((total / temp.Count()), 2).ToString() == "NaN" ? 0 : Math.Round((total / temp.Count()), 2);
                return tres;
            };

            // 计算
            double studentScore = getAvgScore(ENUM_EVA_TYPE.Student);
            double departmentScore = getAvgScore(ENUM_EVA_TYPE.Department);
            double teacherScore = getAvgScore(ENUM_EVA_TYPE.Teacher);
            double courseLeaderScore = getAvgScore(ENUM_EVA_TYPE.CourseLeader);
            double selfScore = getAvgScore(ENUM_EVA_TYPE.Self);

            var res = Math.Round((double) (studentScore * eva.SHeight), 2) +
                      Math.Round((double) (departmentScore * eva.DHeight), 2) +
                      Math.Round((double) (teacherScore * eva.THeight), 2) +
                      Math.Round((double) (courseLeaderScore * eva.LHeight), 2) +
                      Math.Round((double) (eva.MHeight * selfScore), 2);
            return res;
        }
        
        /// <summary>
        /// 获取最终比重
        /// </summary>
        Func<ENUM_EVA_TYPE, int, double> getHeight = (type_Role, evaID) =>
        {
            using (MyDB db = new MyDB())
            {
                double Height = 0;
                EvaluaHeight eva = db.EvaluaHeight.Find(evaID);
                if (type_Role == ENUM_EVA_TYPE.Student)
                {
                    Height = (double)eva.SHeight;
                }
                if (type_Role == ENUM_EVA_TYPE.Teacher)
                {
                    Height = (double)eva.THeight;
                }
                if (type_Role == ENUM_EVA_TYPE.CourseLeader)
                {
                    Height = (double)eva.LHeight;
                }
                if (type_Role == ENUM_EVA_TYPE.Department)
                {
                    Height = (double)eva.DHeight;
                }
                if (type_Role == ENUM_EVA_TYPE.Self)
                {
                    Height = (double)eva.MHeight;
                }

                return Height;
            }

        };

        #endregion

        #region 公用方法

         /// <summary>
        /// 获取总成绩
        /// </summary>
        /// <param name="evaId"></param>
        /// <param name="answerList"></param>
        /// <param name="question"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        private double getScore(List<int> answerList, List<int> question)
        {
            using (MyDB db = new MyDB())
            {
                double total = 0;
                for (int i = 0; i < answerList.Count; i++)
                {
                    var q = db.Question.Find(question[i]);
                    total += answerList[i] * getTotalScore(q);
                }

                return Math.Round(total,2);
            }
        }

        /// <summary>
        /// 递归
        /// </summary>
        /// <param name="q"></param>
        /// <returns></returns>
        private double getTotalScore(Question q)
        {
            double? t = q.Height;
            if (q.Parent != null)
            {
                t = t * getTotalScore(q.Parent);
            }

            return Math.Round((double) t,5);
        }

        #endregion
    }
}
